/*
 * Copyright (c) 2010 Philip Foulkes <philip.foulkes@gmail.com>
 *
 * This file is free software and is made available under the 
 * terms of the GNU General Public License, version 2, or at your
 * option, any later version
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#ifndef _MRP_STATE_MACHINE_H_
#define _MRP_STATE_MACHINE_H_

/**
*   Protocol events
*/
#define MRP_PROTO_EVENT_MIN				0 // This needs to be set to the value of the first item in the list 
#define MRP_PROTO_EVENT_Begin 			0
#define MRP_PROTO_EVENT_New				1
#define MRP_PROTO_EVENT_Join			2
#define MRP_PROTO_EVENT_Lv				3
#define MRP_PROTO_EVENT_tx				4
#define MRP_PROTO_EVENT_txLA			5
#define MRP_PROTO_EVENT_txLAF			6
#define MRP_PROTO_EVENT_rNew			7
#define MRP_PROTO_EVENT_rJoinIn			8
#define MRP_PROTO_EVENT_rIn				9
#define MRP_PROTO_EVENT_rJoinMt			10
#define MRP_PROTO_EVENT_rMt				11
#define MRP_PROTO_EVENT_rLv				12
#define MRP_PROTO_EVENT_rLA				13
#define MRP_PROTO_EVENT_Flush			14
#define MRP_PROTO_EVENT_ReDeclare		15
#define MRP_PROTO_EVENT_periodic		16
#define MRP_PROTO_EVENT_periodicEnable	17
#define MRP_PROTO_EVENT_periodicDisable	18
#define MRP_PROTO_EVENT_leavetimer		19
#define MRP_PROTO_EVENT_leavealltimer	20
#define MRP_PROTO_EVENT_periodictimer	21
#define MRP_PROTO_EVENT_MAX				21 // This needs to be set to the value of the last item in this list 
#define MRP_PROTO_EVENT_NUM 			(MRP_PROTO_EVENT_MAX - MRP_PROTO_EVENT_MIN + 1)

/**
*   Protocol actions
*/
#define MRP_PROTO_ACTION_MIN				25
#define MRP_PROTO_ACTION_NONE				25
#define MRP_PROTO_ACTION_New				26
#define MRP_PROTO_ACTION_Join				27
#define MRP_PROTO_ACTION_Lv					28
#define MRP_PROTO_ACTION_sN					29
#define MRP_PROTO_ACTION_sJ					30
#define MRP_PROTO_ACTION_sL					31
#define MRP_PROTO_ACTION_s					32
#define MRP_PROTO_ACTION_ABsAB 				33  // AB: Angle Bracket
#define MRP_PROTO_ACTION_ABsLAB				34  // AB: Angle Bracket
#define MRP_PROTO_ACTION_ABsJAB				35
#define MRP_PROTO_ACTION_sLA				36
#define MRP_PROTO_ACTION_periodic			37
#define MRP_PROTO_ACTION_START_leavetimer	38
#define MRP_PROTO_ACTION_STOP_leavetimer	39
#define MRP_PROTO_ACTION_START_leavealltimer 40
#define MRP_PROTO_ACTION_START_periodictimer 41
#define MRP_PROTO_ACTION_x					42
#define MRP_PROTO_ACTION_MAX				42
#define MRP_PROTO_ACTION_NUM				(MRP_PROTO_ACTION_MAX - MRP_PROTO_ACTION_MIN + 1)

/**
* Timer operations
*/

#define MRP_STATE_MACHINE_SET_UP_STATE_1(state) \
	MRP_STATE_MACHINE_SET_UP_STATE_3(state,MRP_PROTO_ACTION_NONE,MRP_PROTO_ACTION_NONE)
	
#define MRP_STATE_MACHINE_SET_UP_STATE_2(state,protocol_action1) \
	MRP_STATE_MACHINE_SET_UP_STATE_3(state,protocol_action1,MRP_PROTO_ACTION_NONE)

#define MRP_STATE_MACHINE_SET_UP_STATE_3(state,protocol_action1,protocol_action2) \
	((state) | (protocol_action1 << 8) | (protocol_action2 << 16))

#define mrp_get_state(state_and_action_value) 				((state_and_action_value & 0x0000FF))
#define mrp_get_protocol_action1(state_and_action_value) 	((state_and_action_value & 0x00FF00) >> 8)
#define mrp_get_protocol_action2(state_and_action_value) 	((state_and_action_value & 0xFF0000) >> 16)

#define mrp_get_new_state(state_table,proto_event,min_proto_event,max_proto_event,state,min_state,max_state,no_state_change) \
	(proto_event >= MRP_PROTO_EVENT_MIN && proto_event <= MRP_PROTO_EVENT_MAX && state >= min_state && state <= max_state) ? \
		(mrp_get_state (state_table [mrp_protocol_event - min_proto_event][state - min_state]) == no_state_change) ? \
			state : \
		(mrp_get_state (state_table [mrp_protocol_event - min_proto_event][state - min_state])) : \
	-EINVAL
	
#define mrp_get_new_protocol_action1(state_table,proto_event,min_proto_event,max_proto_event,state,min_state,max_state) \
	(proto_event >= MRP_PROTO_EVENT_MIN && proto_event <= MRP_PROTO_EVENT_MAX && state >= min_state && state <= max_state) ? \
		(mrp_get_protocol_action1 (state_table [mrp_protocol_event - min_proto_event][state - min_state]) == 0) ? \
			MRP_PROTO_ACTION_NONE : \
		(mrp_get_protocol_action1 (state_table [mrp_protocol_event - min_proto_event][state - min_state])) : \
	-EINVAL
	
#define mrp_get_new_protocol_action2(state_table,proto_event,min_proto_event,max_proto_event,state,min_state,max_state) \
	(proto_event >= MRP_PROTO_EVENT_MIN && proto_event <= MRP_PROTO_EVENT_MAX && state >= min_state && state <= max_state) ? \
		(mrp_get_protocol_action2 (state_table [mrp_protocol_event - min_proto_event][state - min_state]) == 0) ? \
			MRP_PROTO_ACTION_NONE : \
		(mrp_get_protocol_action2 (state_table [mrp_protocol_event - min_proto_event][state - min_state])) : \
	-EINVAL

struct mrp_state_machine;

typedef	int (*mrp_state_machine_perform_protocol_action)  (struct mrp_state_machine * mrp_state_machine, int protocol_action);
typedef int (*mrp_state_machine_state_changed)			  (struct mrp_state_machine * mrp_state_machine, int old_state, int new_state);
typedef int (*mrp_state_machine_get_new_state) 			  (int mrp_protocol_event, int mrp_state_machine_current_state, int * mrp_new_state_machine_state);
typedef	int (*mrp_state_machine_get_new_protocol_action1) (int mrp_protocol_event, int mrp_state_machine_current_state, int * mrp_new_state_machine_protocol_action);
typedef	int (*mrp_state_machine_get_new_protocol_action2) (int mrp_protocol_event, int mrp_state_machine_current_state, int * mrp_new_state_machine_protocol_action);

struct mrp_state_machine_ops
{
	mrp_state_machine_perform_protocol_action 		mrp_state_machine_perform_protocol_action;
	mrp_state_machine_state_changed					mrp_state_machine_state_changed;
	mrp_state_machine_get_new_state 				mrp_state_machine_get_new_state;
	mrp_state_machine_get_new_protocol_action1 		mrp_state_machine_get_new_protocol_action1;
	mrp_state_machine_get_new_protocol_action2 		mrp_state_machine_get_new_protocol_action2;
};

struct mrp_state_machine
{
	int current_state;
	char * name;
	
	struct mrp_state_machine_ops * mrp_state_machine_ops;
	struct list_head request_opportunity_list;
};

struct mrp_state_machine * mrp_state_machine_create_and_init (int current_state, struct mrp_state_machine_ops * mrp_state_machine_ops, char * name);
void mrp_state_machine_destroy (struct mrp_state_machine ** mrp_state_machine);

int mrp_state_machine_init (struct mrp_state_machine * mrp_state_machine, int current_state, struct mrp_state_machine_ops * mrp_state_machine_ops, char * name);
void mrp_state_machine_clean_up (struct mrp_state_machine * mrp_state_machine);

int mrp_state_machine_get_current_state (struct mrp_state_machine * mrp_state_machine);

int mrp_state_machine_receive_protocol_event (struct mrp_state_machine * mrp_state_machine, int mrp_protocol_event);

#endif
